﻿namespace In66.Net.Share.Application.Caching
{
    public abstract class AbstractCacheService : ICachePreheatable
    {
        protected virtual ICacheProvider CacheProvider { get; private set; }
        protected virtual IServiceProvider ServiceProvider { get; private set; }
        protected virtual In66.Net.Mapper.IObjectMapper Mapper { get; private set; }

        protected AbstractCacheService(ICacheProvider cacheProvider, IServiceProvider serviceProvider)
        {
            CacheProvider = cacheProvider;
            ServiceProvider = serviceProvider;
            Mapper = ServiceProvider.GetRequiredService<In66.Net.Mapper.IObjectMapper>();
        }

        public abstract Task PreheatAsync();

        public virtual string ConcatCacheKey(params object[] items)
        {
            if (items.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(items));
            }

            return string.Join(CommonCachingConsts.LinkChar, items);
        }

        public virtual async Task RemoveCachesAsync(Func<CancellationToken, Task> dataOperater, params string[] cacheKeys)
        {
            var pollyTimeoutSeconds = CacheProvider.RedisOptions.Value.PollyTimeoutSeconds;
            var keyExpireSeconds = pollyTimeoutSeconds + 1;

            await CacheProvider.KeyExpireAsync(cacheKeys, keyExpireSeconds);

            var expireDt = DateTime.Now.AddSeconds(keyExpireSeconds);
            var cancelTokenSource = new CancellationTokenSource();
            var timeoutPolicy = Policy.TimeoutAsync(pollyTimeoutSeconds, Polly.Timeout.TimeoutStrategy.Optimistic);
            await timeoutPolicy.ExecuteAsync(async (cancellToken) =>
            {
                await dataOperater(cancellToken);
                cancellToken.ThrowIfCancellationRequested();
            }, cancelTokenSource.Token);

            try
            {
                await CacheProvider.RemoveAllAsync(cacheKeys);
            }
            catch (Exception ex)
            {
                LocalVariables.Instance.Queue.Enqueue(new LocalVariables.Model(cacheKeys, expireDt));
                var logger = ServiceProvider.GetRequiredService<Lazy<ILogger<AbstractCacheService>>>();
                logger.Value.LogError(ex, ex.Message);
            }
        }
    }
}
